上一篇我們設計了 I2C Master 的狀態機,那麼我們今天要來引用上次完成的狀態機模塊來實現 I2C Master 模塊,但我們先一步一步來,先來完成 write 的部分,也就是先可以把輸入的值成功以 I2C 協定傳出去,再來完成讀入的部分
先來看看這個 I2C Master write 模塊該有哪些輸入輸出腳吧:
輸入:
輸出:
輸入+輸出:
module I2C_write_R_W(
clk_sys,
rst_n,
en,
data_R,
data_W,
ACK,
ACK1,
ACK2,
ACK3,
rstACK,
SCLK,
SDA,
ldnACK1,
ldnACK2,
ldnACK3,
R_W,
tick_I2C_neg
);
/*---------ports declaration---------*/
input clk_sys;
input rst_n;
input en;
input R_W;
input [26:0] data_R;//read 27bit
input [17:0] data_W;//write 18bit
output ACK1;
output ACK2;
output ACK3;
output tick_I2C_neg;
output ACK;
output rstACK;
output SCLK;
output ldnACK1;
output ldnACK2;
output ldnACK3;
reg tick_I2C_neg;
reg ACK1;
reg ACK2;
reg ACK3;
wire ACK;
wire rstACK;
wire SCLK;
wire ldnACK1;
wire ldnACK2;
wire ldnACK3;
inout SDA;
宣告變數:
需要用到的變數有:
/*---------variables---------*/
reg [4:0] count;
reg [8:0] cnt_I2C;
reg tick_I2C;
reg SCLK_100k;
reg [26:0] regdata_R;
reg [17:0] regdata_W;
wire SCLK_temp;
wire SHEN;
wire LDEN;
wire SDO;
wire countEN;
wire rstcount;
wire SEL;
/*---------assign wire---------*/
assign SEL = (SHEN)?((!R_W)?(regdata_W[17]):(regdata_R[26])):(SDO);
assign SDA = (SEL)?(1'bz):(1'b0);
assign ACK = ACK1|ACK2|ACK3;
引用狀態機模組
/*---------module instantiate---------*/
I2C_control_R_W U0(
.clk_sys(clk_sys),
.SCLK_100k(SCLK_100k),
.tick_I2C(tick_I2C),
.rst_n(rst_n),
.en(en),
.count(count),
.countEN(countEN),
.rstcount(rstcount),
.ACK1(ldnACK1),
.ACK2(ldnACK2),
.ACK3(ldnACK3),
.rstACK(rstACK),
.SCLK(SCLK),
.SCLK_temp(SCLK_temp),
.SHEN(SHEN),
.LDEN(LDEN),
.SDO(SDO),
.R_W(R_W)
);
cnt_I2C
/*---------100k counter---------*/
always@(posedge clk_sys or negedge rst_n)begin
if(!rst_n)cnt_I2C <= 9'd0;
else begin
if(cnt_I2C<9'd499)cnt_I2C <= cnt_I2C + 9'd1;//0-499
else cnt_I2C <= 9'd0;
end
end
tick_I2C
/*---------I2C tick---------*/
always@(posedge clk_sys or negedge rst_n)begin
if(!rst_n)tick_I2C <= 1'b0;
else begin
if(cnt_I2C==9'd498)tick_I2C <= 1'b1;
else tick_I2C <= 1'b0;
end
end
/*---------I2C tick_neg---------*/
always@(posedge clk_sys or negedge rst_n)begin
if(!rst_n)tick_I2C_neg <= 1'b0;
else begin
if(cnt_I2C==9'd249)tick_I2C_neg <= 1'b1;
else tick_I2C_neg <= 1'b0;
end
end
SCLK_100k
/*---------SCLK_100k---------*/
always@(posedge clk_sys or negedge rst_n)begin
if(!rst_n)SCLK_100k <= 1'b0;
else begin
if(cnt_I2C==9'd100) SCLK_100k <= 1'b0;
else if(cnt_I2C==9'd400)SCLK_100k <= 1'b1;
else SCLK_100k <= SCLK_100k;
end
end
這裡來解釋一下為什麼是 100 跟 400:
還記得前幾天的 I2C 的介紹內有提到,SDA 只能在 SCLK 為 "0" 時更動值,因此我們的 SCLK 高電位的部分要抓短一點,所以才會出現 100 時下去 400 上來的情況(咦?這樣不是反過來嗎?因為我狀態機模組裡面有反向了,你們也可以 100 上去 400 下來,然後不要反向,當然你想用其他的高電位寬度也都可以!),而 SDA 只會在 cnt_I2C 等於 498 時才更動值,如此一來就可以達到 SDA 只會在 SCLK 等於 "0" 時才更動值的效果!
藉由 rstcount&countEN 來控制 count
/*---------count---------*/
always@(posedge clk_sys or negedge rst_n)begin
if(!rst_n)begin
count <= 5'd0;
end
else begin
if(tick_I2C)begin
if(rstcount) count <= 5'd0;
else if(countEN)count <= count + 5'd1;
else count <= count;
end
else count <= count;
end
end
藉由 LDEN&SHEN&R_W 來控制我們的 regdata_W®data_R
/*---------load data---------*/
always@(posedge clk_sys or negedge rst_n)begin
if(!rst_n)begin
regdata_R <= 27'd0;
regdata_W <= 18'd0;
end
else begin
if(!R_W)begin
if(tick_I2C)begin
if(LDEN) regdata_W <= data_W;
else if(SHEN)regdata_W <= {regdata_W[16:0],1'b0};
else regdata_W <= regdata_W;
end
else regdata_W <= regdata_W;
end
else begin
if(tick_I2C)begin
if(LDEN) regdata_R <= data_R;
else if(SHEN)regdata_R <= {regdata_R[25:0],1'b0};
else regdata_R <= regdata_R;
end
else regdata_R <= regdata_R;
end
end
end
讀取 BUS 上的 ACK
/*---------ACK---------*/
always@(posedge clk_sys or negedge rst_n)begin
if(!rst_n)begin
ACK1 <= 1'b0;
ACK2 <= 1'b0;
ACK3 <= 1'b0;
end
else if(rstACK&&tick_I2C)begin
ACK1 <= 1'b0;
ACK2 <= 1'b0;
ACK3 <= 1'b0;
end
else begin
if(ldnACK1&&tick_I2C)ACK1 <= SDA;
else ACK1 <= ACK1;
if(ldnACK2&&tick_I2C)ACK2 <= SDA;
else ACK2 <= ACK2;
if(ldnACK3&&tick_I2C)ACK3 <= SDA;
else ACK3 <= ACK3;
end
end
endmodule
由於這篇的內容也算偏多,可以先讓你們消化一下,下一篇我們會繼續完成 Read 的部分,並且撰寫 TestBench 來驗證電路的正確性~~